﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using AliCMS.SystemModule;

namespace AliCMS
{
	internal class Upload
	{
        internal Upload(string uploadId)
        {
			_uploadId = uploadId;
            _files = new Dictionary<string, string>();
            _total = 0;
            _process = 0;
            _file = null;
        }

		internal void Do(HttpApplication application)
		{
			Manager manager = (Manager)application.Application["Manager"];

			application.Server.ScriptTimeout = 24 * 60 * 60;

			IServiceProvider provider = (IServiceProvider)HttpContext.Current;
			HttpWorkerRequest wr = (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));

			#region 必须有内容
			if (!wr.HasEntityBody())
			{
				if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
					application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:2,message:\"No entity body!\"});</script>");
				else
					application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":2,\"message\":\"No entity body!\"}");
				return;
			}
			#endregion

			#region 不是来自客户端时，必须有来源
			if (String.IsNullOrEmpty(application.Request.QueryString["CID"]) && application.Request.UrlReferrer == null)
			{
				if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
					application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:3,message:\"Must have referer!\"});</script>");
				else
					application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":3,\"message\":\"Must have referer!\"}");
				return;
			}
			#endregion

			#region 必须有Object头
			string objectName = application.Request.QueryString["Object"];
			if (String.IsNullOrEmpty(objectName))
			{
				if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
					application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:4,message:\"Object not set!\"});</script>");
				else
					application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":4,\"message\":\"Object not set!\"}");
				return;
			}
			#endregion

			#region 必须有Operation头
			string operation = application.Request.QueryString["Operation"];
			if (String.IsNullOrEmpty(operation))
			{
				if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
					application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:5,message:\"Operation not set!\"});</script>");
				else
					application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":5,\"message\":\"Operation not set!\"}");
				return;
			}
			#endregion

			#region 来源跟请求地址必须同一个域名下面
			if (String.IsNullOrEmpty(application.Request.QueryString["CID"]))//不来自客户端的情况下
			{
				string root = application.Request.Url.Scheme + "://" + application.Request.Url.Host + (application.Request.Url.IsDefaultPort ? "/" : ":" + application.Request.Url.Port + "/");
				if (!application.Request.UrlReferrer.ToString().StartsWith(root, StringComparison.InvariantCultureIgnoreCase))
				{
					if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
						application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:6,message:\"Must be same domain!\"});</script>");
					else
						application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":6,\"message\":\"Must be same domain!\"}");
					return;
				}
			}
			#endregion

			#region 获取实例
			int instanceID = -1;
			if (!String.IsNullOrEmpty(application.Request.QueryString["Instance"]))
				instanceID = Convert.ToInt32(application.Request.QueryString["Instance"]);
			#endregion

			#region 获取语言
			string lang = null;
			if (!String.IsNullOrEmpty(application.Request.QueryString["Lang"]))
				lang = application.Request.QueryString["Lang"];
			#endregion

			#region 实例或语言没有指定的情况下，通过来源获取
			DataRow channel = null;
			if (lang == null || instanceID == -1)
			{
				string path = null;

				//获取路径
				string referrer = application.Request.UrlReferrer.ToString();
				if (application.Request.UrlReferrer.LocalPath.StartsWith("/debug/"))
					referrer = referrer.Replace("/debug/", "");

				//从设定域名的栏目中查找栏目
				DataRow[] rows = manager.Channels.Select(Alias.Channel.Domain + " IS NOT NULL");
				foreach (DataRow row in rows)
				{
					string domain = String.Empty;
					string[] domains = row[Alias.Channel.Domain].ToString().Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
					foreach (string d in domains)
					{
						if (referrer.StartsWith(d, StringComparison.InvariantCultureIgnoreCase))
						{
							if (channel == null || domain.Length < d.Length)
							{
								channel = row;
								domain = d;
							}
						}
					}
				}

				//如果没找到，再正常查找栏目
				if (channel == null)
				{
					path = application.Request.UrlReferrer.LocalPath;
					//注：不再考虑Fake404。彻底没有问题时，才移除下面注释的代码
					//if (application.Request.UrlReferrer.Query.StartsWith("?/"))//fake404
					//	path = application.Request.UrlReferrer.Query.Substring(1);

					channel = manager.GetChannel(path);
				}

				//如果还是没找到，说明没对应栏目，直接返回
				if (channel == null)
				{
					if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
						application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:7,message:\"no channel!\"});</script>");
					else
						application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":7,\"message\":\"no channel!\"}");
					return;
				}

				if (lang == null)
					lang = channel[Alias.Channel.Lang].ToString();

				//如果指定Mark，则查找当前栏目以Mark绑定的栏目
				DataRow targetChannel = channel;
				if (!String.IsNullOrEmpty(application.Request.QueryString["Mark"]))
				{
					targetChannel = manager.GetExtraInstance(channel, application.Request.QueryString["Mark"]);

					//如果没有对应栏目，则直接返回
					if (targetChannel == null)
					{
						if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
							application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:7,message:\"no channel!\"});</script>");
						else
							application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":7,\"message\":\"no channel!\"}");
						return;
					}
				}

				if (instanceID == -1)
				{
					if (targetChannel.Table.Columns.Contains(SystemModule.Alias.Template.IsAdmin) && Convert.ToBoolean(targetChannel[SystemModule.Alias.Template.IsAdmin]))
					{
						if (path == null)
							path = application.Request.Path;

						string p = targetChannel[Alias.Channel.Path].ToString();
						if (p.IndexOf('{') != -1)
						{
							Match match = Regex.Match(p, @"\{(?<name>\w+)(:(?<regex>.*?))?(?<!\\)\}");

							p = p.Remove(match.Index, match.Length).Insert(match.Index, "(?<v>" + match.Groups["regex"].Value.Replace(@"\}", "}") + ")");
							match = Regex.Match(path, p);
							p = match.Value;
						}

						path = path.Substring(p.Length);
						if (instanceID == -1 && Regex.IsMatch(path, @"^\d+/"))
							instanceID = Convert.ToInt32(path.Substring(0, path.IndexOf('/')));
					}
					else if (!Convert.IsDBNull(targetChannel[Alias.Instance.InnerID]))
					{
						instanceID = (int)targetChannel[Alias.Instance.InnerID];
					}

					if (instanceID == -1)
					{
						if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
							application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:8,message:\"no instance!\"});</script>");
						else
							application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":8,\"message\":\"no instance!\"}");
						return;
					}
				}
			}
			#endregion

			#region 检查权限
			#region 是否检查权限
			bool validate = true;
			if (!String.IsNullOrEmpty(application.Request.QueryString["Validate"]) && !Convert.ToBoolean(application.Request.QueryString["Validate"]))
			{
				string key = application.Request.QueryString["ValidateKey"];
				if (!String.IsNullOrEmpty(key))
					validate = !manager.IsCorrectValidateKey(key);
			}
			#endregion

			#region 处理来自客户端的请求
			if (!String.IsNullOrEmpty(application.Request.QueryString["CID"]))
			{
				String cid = application.Request.QueryString["CID"];

				//检查Sign
				string sign = application.Request.QueryString["Sign"];
				if (String.IsNullOrEmpty(sign))
				{
					application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":12,\"message\":\"必须指定sign！\"}");
					return;
				}

				//检查客户端是否存在
				DataRow[] rows = manager.Clients.Select(Alias.Client.ID + "='" + (new Guid(cid)).ToString() + "'");
				if (rows.Length == 0)
				{
					application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":13,\"message\":\"指定客户端不存在！\"}");
					return;
				}

				//检查签名是否正确
				DataRow client = rows[0];
				string secret = client[Alias.Client.Secret].ToString();
				if (sign != System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(cid + secret, "md5"))
				{
					application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":14,\"message\":\"签名不正确！\"}");
					return;
				}

				validate = false;
			}
			#endregion

			if (validate)
			{
				bool hasRight = manager.HasRight(null, instanceID, lang, objectName, operation, Guid.Empty);
				if (!hasRight)
				{
					if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
						application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:9,message:\"no right!\"});</script>");
					else
						application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":9,\"message\":\"no right!\"}");
					return;
				}
			}
			#endregion

			#region PageLang
			string pageLang = lang;
			if (String.IsNullOrEmpty(application.Request.QueryString["CID"]))//不来自客户端
			{
				if (channel == null)
				{
					channel = manager.GetChannel(application.Request.UrlReferrer.LocalPath);
					if (channel == null)
					{
						if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
							application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:7,message:\"no channel!\"});</script>");
						else
							application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":7,\"message\":\"no channel!\"}");
						return;
					}
				}
				pageLang = channel[Alias.Channel.Lang].ToString();
			}
			#endregion

			#region 来自客户端时，直接保存，并返回
			if (!String.IsNullOrEmpty(application.Request.QueryString["CID"]))
			{
				HttpPostedFile file = application.Request.Files[0];

				#region 检查是否超过最大限制
				if (!String.IsNullOrEmpty(application.Request.QueryString["MaxLength"]) && long.Parse(application.Request.QueryString["MaxLength"]) > file.ContentLength)
				{
					application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":6,\"message\":\"File size exceeds the max length!\"}");
					return;
				}
				#endregion

				#region 检查文件类型
				string contentType = file.ContentType;
				string extention = Path.GetExtension(file.FileName).ToLower();
				
				if (!Regex.IsMatch(manager.Site[Alias.InstanceConfig.UploadType + pageLang], "^" + extention.Replace(".", @"\.") + @" *:.*" + contentType.Replace(".", @"\.") + @".*$", RegexOptions.Multiline))
				{
					string message = null;
					using (SystemModule.Module.IDictionary dictBll = (SystemModule.Module.IDictionary)manager.CreateObject("Module.Dictionary", 0, pageLang, false))
					{
						Dictionary<string, object> word = dictBll.Get("AliCMS.SystemModule", "lang_upload_file_type");
						if (word != null)
							message = word[Alias.Module.Dictionary.Content].ToString().Replace("{#1#}", extention).Replace("{#2#}", contentType);
						else
							message = "upload file type error!";
					}

					application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":11,\"message\":\"" + manager.Escape(message) + "\"}");
					return;
				}
				#endregion

				#region 构造相对路径
				string relativePath = manager.GetDataPath(instanceID, lang);
				if (!String.IsNullOrEmpty(application.Request.QueryString["SubInstanceID"]))
					relativePath += Regex.Replace(application.Request.QueryString["SubInstanceID"].Replace("-", ""), "^([0-9a-f]{3})([0-9a-f]{3})(.*)$", "$1/$2/$3/");
				string folder = application.Request.QueryString["Folder"];
				if (!String.IsNullOrEmpty(folder))
				{
					if (folder.StartsWith("/"))
						relativePath = folder;
					else
						relativePath += folder;

					relativePath = relativePath.TrimEnd('/') + '/';
				}
				manager.MakeSureFolderExist(manager.MapPath(relativePath));

				#region id
				string id = application.Request.QueryString["ID"];
				if (!String.IsNullOrEmpty(id))
					id += "_";
				#endregion

				if (String.IsNullOrEmpty(application.Request.QueryString["NameBySystem"]) || Convert.ToBoolean(application.Request.QueryString["NameBySystem"]))
					relativePath += id + DateTime.Now.ToString("yyyyMMddHHmmssfff") + extention;
				else
					relativePath += Path.GetFileName(file.FileName);
				#endregion

				#region 构造绝对路径
				string absolutePath = manager.MapPath(relativePath);
				if (File.Exists(absolutePath))
				{
					if (Convert.ToBoolean(application.Request.QueryString["Overwrite"]))
					{
						File.Delete(absolutePath);
					}
					else
					{
						int counter = 0;
						do
						{
							counter++;

							string name = Path.GetFileNameWithoutExtension(relativePath);
							Match match = Regex.Match(name, @"\(" + (counter - 1) + @"\)$");
							if (match.Success)
								name = name.Remove(match.Index);
							relativePath = Path.Combine(Path.GetDirectoryName(relativePath), name + "(" + counter + ")" + extention).Replace('\\', '/');
							absolutePath = wr.MapPath(relativePath);
						}
						while (File.Exists(absolutePath));

						relativePath = relativePath.Replace('\\', '/');
					}
				}
				#endregion

				file.SaveAs(absolutePath);

				#region 生成小图
				string thumbFile = null;
				if (!String.IsNullOrEmpty(application.Request.QueryString["MakeThumbnail"]) && Convert.ToBoolean(application.Request.QueryString["MakeThumbnail"]))
				{
					int width = 100;
					if (!String.IsNullOrEmpty(application.Request.QueryString["ThumbnailWidth"]))
						width = Convert.ToInt32(application.Request.QueryString["ThumbnailWidth"]);

					int height = 100;
					if (!String.IsNullOrEmpty(application.Request.QueryString["ThumbnailHeight"]))
						height = Convert.ToInt32(application.Request.QueryString["ThumbnailHeight"]);

					thumbFile = Path.GetDirectoryName(relativePath).Replace("\\", "/") + '/' + Path.GetFileNameWithoutExtension(relativePath) + "_small" + Path.GetExtension(relativePath);

					Bitmap sourceImage = new Bitmap(absolutePath);
					Bitmap targetImage = new Bitmap(width, height);

					Graphics g = Graphics.FromImage(targetImage);
					g.InterpolationMode = InterpolationMode.HighQualityBicubic;
					g.DrawImage(sourceImage, new Rectangle(0, 0, width, height), new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), GraphicsUnit.Pixel);
					g.Flush();
					g.Dispose();
					sourceImage.Dispose();

					targetImage.Save(manager.MapPath(thumbFile));
					targetImage.Dispose();
				}
				#endregion

				application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":0,\"path\":\"" + relativePath + "\"" + (thumbFile == null ? "" : ",\"thumbnail\":\"" + thumbFile + "\"") + "}");
				application.Application.Remove(_uploadId);
				return;
			}
			#endregion

			Dictionary<string, string> files = new Dictionary<string, string>();
			int index = 0;//检索到的位置
			int state = 0;//0：未开始；1：字段开始；2：值开始；3：字段结束

			int fieldStartIndex = -1;
			int fieldEndIndex = -1;
			int dataStartIndex = -1;
			string headerContentType = wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentType);
			string boundary = "--" + headerContentType.Substring(headerContentType.LastIndexOf("boundary=") + 9);
			byte[] fieldStartMark = Encoding.UTF8.GetBytes(boundary);
			byte[] fieldEndMark = Encoding.UTF8.GetBytes("\r\n" + boundary);
			byte[] dataStartMark = Encoding.UTF8.GetBytes("\r\n\r\n");
			_total = wr.GetTotalEntityBodyLength();

			#region 检查是否超过最大限制
			if (!String.IsNullOrEmpty(application.Request.QueryString["MaxLength"]) && long.Parse(application.Request.QueryString["MaxLength"]) > _total)
			{
				if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
					application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:6,message:\"File size exceeds the max length!\"});</script>");
				else
					application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":6,\"message\":\"File size exceeds the max length!\"}");
				return;
			}
			#endregion

			int bufferSize = wr.GetPreloadedEntityBodyLength();
			if (bufferSize == 0)
				bufferSize = 51200;//50k
			byte[] buffer = new byte[bufferSize];
			int read = wr.GetPreloadedEntityBody(buffer, 0);
			_process = read;

			while (true)
			{
				fieldStartIndex = FindBytes(buffer, fieldStartMark, index);
				if (fieldStartIndex == -1)
					break;

				state = 1;
				index = fieldStartIndex + boundary.Length;
				dataStartIndex = FindBytes(buffer, dataStartMark, index);
				if (dataStartIndex == -1)
					break;

				state = 2;
				index = dataStartIndex = dataStartIndex + dataStartMark.Length;

				string startToData = Encoding.UTF8.GetString(buffer, fieldStartIndex, dataStartIndex - fieldStartIndex);
				Match match = Regex.Match(startToData, "filename=\"(?<v>.*?)\"");
				if (!match.Success || String.IsNullOrEmpty(match.Groups["v"].Value))
				{
					state = 0;
					continue;
				}

				string sourceFile = match.Groups["v"].Value;
				match = Regex.Match(startToData, @"Content-Type:\s(?<v>.*?)(\s|;|$)");
				if (!match.Success)
				{
					state = 0;
					continue;
				}

				string contentType = match.Groups["v"].Value.ToLower();
				string extention = Path.GetExtension(sourceFile).ToLower();

				#region 检查文件类型
				if (!Regex.IsMatch(manager.Site[Alias.InstanceConfig.UploadType + pageLang], "^" + extention.Replace(".", @"\.") + @" *:.*" + contentType.Replace(".", @"\.") + @".*$", RegexOptions.Multiline))
				{
					string message = null;
					using (SystemModule.Module.IDictionary dictBll = (SystemModule.Module.IDictionary)manager.CreateObject("Module.Dictionary", 0, pageLang, false))
					{
						Dictionary<string, object> word = dictBll.Get("AliCMS.SystemModule", "lang_upload_file_type");
						if (word != null)
							message = word[Alias.Module.Dictionary.Content].ToString().Replace("{#1#}", extention).Replace("{#2#}", contentType);
						else
							message = "upload file type error!";
					}

					if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
						application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:11,message:\"" + manager.Escape(message) + "\"});</script>");
					else
						application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":11,\"message\":\"" + manager.Escape(message) + "\"}");
					return;
				}
				#endregion

				#region 构造相对路径
				string relativePath = manager.GetDataPath(instanceID, lang);
				if (!String.IsNullOrEmpty(application.Request.QueryString["SubInstanceID"]))
					relativePath += Regex.Replace(application.Request.QueryString["SubInstanceID"].Replace("-", ""), "^([0-9a-f]{3})([0-9a-f]{3})(.*)$", "$1/$2/$3/");
				string folder = application.Request.QueryString["Folder"];
				if (!String.IsNullOrEmpty(folder))
				{
					if (folder.StartsWith("/"))
						relativePath = folder;
					else
						relativePath += folder;
					
					relativePath = relativePath.TrimEnd('/') + '/';
				}
				manager.MakeSureFolderExist(manager.MapPath(relativePath));

				#region id
				string id = application.Request.QueryString["ID"];
				if (!String.IsNullOrEmpty(id))
					id += "_";
				#endregion

				if (Convert.ToBoolean(application.Request.QueryString["NameBySystem"]))
					relativePath += id + DateTime.Now.ToString("yyyyMMddHHmmssfff") + Path.GetExtension(sourceFile);
				else
					relativePath += Path.GetFileName(sourceFile);
				#endregion

				#region 构造绝对路径
				string absolutePath = wr.MapPath(relativePath);
				if (File.Exists(absolutePath))
				{
					if (Convert.ToBoolean(application.Request.QueryString["Overwrite"]))
					{
						File.Delete(absolutePath);
					}
					else
					{
						int counter = 0;
						do
						{
							counter++;

							string name = Path.GetFileNameWithoutExtension(relativePath);
							match = Regex.Match(name, @"\(" + (counter - 1) + @"\)$");
							if (match.Success)
								name = name.Remove(match.Index);
							relativePath = Path.Combine(Path.GetDirectoryName(relativePath), name + "(" + counter + ")" + extention).Replace('\\', '/');
							absolutePath = wr.MapPath(relativePath);
						}
						while (File.Exists(absolutePath));

						relativePath = relativePath.Replace('\\', '/');
					}
				}
				#endregion

				#region 创建新文件
				string fieldName = null;
				match = Regex.Match(startToData, "name=\"(?<v>.*?)\"");
				if (match.Success)
					fieldName = match.Groups["v"].Value;
				else
					fieldName = DateTime.Now.ToString("yyyyMMddHHmmss");

				NewFile(fieldName, absolutePath, relativePath);
				files[fieldName] = relativePath;
				#endregion

				fieldEndIndex = FindBytes(buffer, fieldEndMark, index);
				if (fieldEndIndex == -1)
					break;

				state = 3;
				index = fieldEndIndex + 1;

				Write(buffer, dataStartIndex, fieldEndIndex - dataStartIndex);
			}

			if (state == 2)
				Write(buffer, dataStartIndex, read - dataStartIndex);

			if (!wr.IsEntireEntityBodyIsPreloaded())
			{
				do
				{
					System.Threading.Thread.Sleep(10);//减少CPU占用率

					read = wr.ReadEntityBody(buffer, bufferSize);
					if (read == 0)
						break;
					_process += read;

					index = 0;
					dataStartIndex = 0;

					while (true)
					{
						if (state == 0 || state == 3)
						{
							fieldStartIndex = FindBytes(buffer, fieldStartMark, index);
							if (fieldStartIndex == -1)
								break;

							state = 1;
							index = fieldStartIndex + boundary.Length;
							dataStartIndex = FindBytes(buffer, dataStartMark, index);
							if (dataStartIndex == -1)
								break;

							state = 2;
							index = dataStartIndex = dataStartIndex + dataStartMark.Length;

							string startToData = Encoding.UTF8.GetString(buffer, fieldStartIndex, dataStartIndex - fieldStartIndex);
							Match match = Regex.Match(startToData, "filename=\"(?<v>.*?)\"");
							if (!match.Success || String.IsNullOrEmpty(match.Groups["v"].Value))
							{
								state = 0;
								continue;
							}

							string sourceFile = match.Groups["v"].Value;
							match = Regex.Match(startToData, @"Content-Type:\s(?<v>.*?)(\s|;|$)");
							if (!match.Success)
							{
								state = 0;
								continue;
							}

							string contentType = match.Groups["v"].Value.ToLower();
							string extention = Path.GetExtension(sourceFile).ToLower();

							#region 检查文件类型
							if (!Regex.IsMatch(manager.Site[Alias.InstanceConfig.UploadType + pageLang], "^" + extention.Replace(".", @"\.") + @" *:.*" + contentType.Replace(".", @"\.") + @".*$", RegexOptions.Multiline))
							{
								string message = null;
								using (SystemModule.Module.IDictionary dictBll = (SystemModule.Module.IDictionary)manager.CreateObject("Module.Dictionary", 0, pageLang, false))
								{
									Dictionary<string, object> word = dictBll.Get("AliCMS.SystemModule", "lang_upload_file_type");
									if (word != null)
										message = word[Alias.Module.Dictionary.Content].ToString().Replace("{#1#}", extention).Replace("{#2#}", contentType);
									else
										message = "upload file type error!";
								}

								if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
									application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:11,message:\"" + manager.Escape(message) + "\"});</script>");
								else
									application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":11,\"message\":\"" + manager.Escape(message) + "\"}");
								return;
							}
							#endregion

							#region 构造相对路径
							string relativePath = manager.GetDataPath(instanceID, lang);
							if (!String.IsNullOrEmpty(application.Request.QueryString["SubInstanceID"]))
								relativePath += Regex.Replace(application.Request.QueryString["SubInstanceID"].Replace("-", ""), "^([0-9a-f]{3})([0-9a-f]{3})(.*)$", "$1/$2/$3/");
							string folder = application.Request.QueryString["Folder"];
							if (!String.IsNullOrEmpty(folder))
							{
								if (folder.StartsWith("/"))
									relativePath = folder;
								else
									relativePath += folder;

								relativePath = relativePath.TrimEnd('/') + '/';
							}
							manager.MakeSureFolderExist(manager.MapPath(relativePath));

							#region id
							string id = application.Request.QueryString["ID"];
							if (!String.IsNullOrEmpty(id))
								id += "_";
							#endregion

							if (Convert.ToBoolean(application.Request.QueryString["NameBySystem"]))
								relativePath += id + DateTime.Now.ToString("yyyyMMddHHmmssfff") + Path.GetExtension(sourceFile);
							else
								relativePath += Path.GetFileName(sourceFile);
							#endregion

							#region 构造绝对路径
							string absolutePath = wr.MapPath(relativePath);
							if (File.Exists(absolutePath))
							{
								if (Convert.ToBoolean(application.Request.QueryString["Overwrite"]))
								{
									File.Delete(absolutePath);
								}
								else
								{
									int counter = 0;
									do
									{
										counter++;

										string name = Path.GetFileNameWithoutExtension(relativePath);
										match = Regex.Match(name, @"\(" + (counter - 1) + @"\)$");
										if (match.Success)
											name = name.Remove(match.Index);
										relativePath = Path.Combine(Path.GetDirectoryName(relativePath), name + "(" + counter + ")" + extention).Replace('\\', '/');
										absolutePath = wr.MapPath(relativePath);
									}
									while (File.Exists(absolutePath));

									relativePath = relativePath.Replace('\\', '/');
								}
							}
							#endregion

							#region 创建新文件
							string fieldName = null;
							match = Regex.Match(startToData, "name=\"(?<v>.*?)\"");
							if (match.Success)
								fieldName = match.Groups["v"].Value;
							else
								fieldName = DateTime.Now.ToString("yyyyMMddHHmmss");

							NewFile(fieldName, absolutePath, relativePath);
							files[fieldName] = relativePath;
							#endregion
						}

						fieldEndIndex = FindBytes(buffer, fieldEndMark, index);
						if (fieldEndIndex == -1)
							break;

						state = 3;
						index = fieldEndIndex + 1;

						Write(buffer, dataStartIndex, fieldEndIndex - dataStartIndex);
					}

					if (state == 2)
						Write(buffer, dataStartIndex, read - dataStartIndex);

					if (!wr.IsClientConnected())
					{
						Cancel();
						break;
					}
				}
				while (read != 0);
			}

			Close();

			#region 生成小图
			string thumbnailFile = null;
			if (Convert.ToBoolean(application.Request.QueryString["MakeThumbnail"]))
			{
				int width = 100;
				if (!String.IsNullOrEmpty(application.Request.QueryString["ThumbnailWidth"]))
					width = Convert.ToInt32(application.Request.QueryString["ThumbnailWidth"]);

				int height = 100;
				if (!String.IsNullOrEmpty(application.Request.QueryString["ThumbnailHeight"]))
					height = Convert.ToInt32(application.Request.QueryString["ThumbnailHeight"]);

				thumbnailFile = Path.GetDirectoryName(_path).Replace("\\", "/") + '/' + Path.GetFileNameWithoutExtension(_path) + "_small" + Path.GetExtension(_path);

				Bitmap sourceImage = new Bitmap(manager.MapPath(_path));
				Bitmap targetImage = new Bitmap(width, height);

				Graphics g = Graphics.FromImage(targetImage);
				g.InterpolationMode = InterpolationMode.HighQualityBicubic;
				g.DrawImage(sourceImage, new Rectangle(0, 0, width, height), new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), GraphicsUnit.Pixel);
				g.Flush();
				g.Dispose();
				sourceImage.Dispose();

				targetImage.Save(manager.MapPath(thumbnailFile));
				targetImage.Dispose();
			}
			#endregion

			#region 加水印
			if (Convert.ToBoolean(application.Request.QueryString["AddWaterMark"]))
			{
				manager.AddWaterMark(manager.MapPath(_path), lang);
			}
			#endregion

			if (!String.IsNullOrEmpty(application.Request.QueryString["Callback"]))
				application.Response.Write("<script>" + application.Request.QueryString["Callback"] + "({uploadId:\"" + _uploadId + "\",error:0,path:\"" + _path + "\"" + (thumbnailFile == null ? "" : ",thumbnail:\"" + thumbnailFile + "\"") + "});</script>");
			else
				application.Response.Write("{\"uploadId\":\"" + _uploadId + "\",\"error\":0,\"path\":\"" + _path + "\"" + (thumbnailFile == null ? "" : ",\"thumbnail\":\"" + thumbnailFile + "\"") + "}");

			application.Application.Remove(_uploadId);
		}
		internal void UpdateState(HttpApplication application)
		{
			application.Response.Expires = 0;
			application.Response.ExpiresAbsolute = DateTime.Now;
			application.Response.AddHeader("pragma", "no-cache");
			application.Response.AddHeader("cache-control", "private");
			application.Response.CacheControl = "no-cache";

			if (_files.Count == 0)
				application.Response.Write("({path:\"\",total:\"0\",process:\"0\"})");
			else
				application.Response.Write("({path:\"" + _path + "\",total:\"" + _total + "\",process:\"" + _process + "\"})");
		}
		internal void Cancel()
		{
			if (_file != null)
				_file.Close();

			foreach (KeyValuePair<string, string> kvp in _files)
			{
				if (File.Exists(kvp.Value))
					File.Delete(kvp.Value);
			}
			_files.Clear();
		}

		private void NewFile(string fieldName, string absolutePath, string relativePath)
        {
            if (_file != null)
                _file.Close();

            _path = relativePath;
            if (_files.ContainsKey(fieldName))
                _files[fieldName + "1"] = absolutePath;
            else
                _files[fieldName] = absolutePath;

            _file = File.Open(absolutePath, FileMode.CreateNew, FileAccess.Write);
        }
		private void Write(byte[] buffer, int offset, int count)
        {
            _file.Write(buffer, offset, count);
        }
		private void Close()
        {
            if (_file != null)
                _file.Close();

            _files.Clear();
        }

		private int FindBytes(byte[] buffer, byte[] search, int startIndex)
		{
			for (int i = startIndex; i < buffer.Length; i++)
			{
				if (i + search.Length > buffer.Length)
					return -1;

				bool found = true;
				for (int j = 0; j < search.Length; j++)
				{
					if (buffer[i + j] != search[j])
					{
						found = false;
						break;
					}
				}

				if (found)
					return i;
			}

			return -1;
		}

        #region Fields
		private string _uploadId;
        private Dictionary<string, string> _files;
        private string _path;
        private long _total;//总字节数
        private long _process;//已上传的字节数
        private FileStream _file;
        #endregion
    }
}
